package com.gf.snoopy.service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.gf.snoopy.analyser.ContentTemplateLoader;
import com.gf.snoopy.constants.SnoopyConstants;
import com.gf.snoopy.constants.enumc.TemplateType;
import com.gf.snoopy.domain.Project;
import com.gf.snoopy.domain.SubProject;
import com.gf.snoopy.domain.Template;
import com.gf.snoopy.dto.DownloadFile;
import com.gf.snoopy.dto.TemplateDto;
import com.gf.snoopy.mapper.ProjectMapper;
import com.gf.snoopy.mapper.SubProjectMapper;
import com.gf.snoopy.mapper.TemplateMapper;
import com.gf.snoopy.platform.exception.PokeErrorCode;
import com.gf.snoopy.platform.exception.PokeException;
import com.gf.snoopy.util.ConvertUtil;
import com.gf.snoopy.util.ParseUtil;
import com.gf.snoopy.util.SnoopyUtil;
import com.gf.snoopy.util.ZipUtil;
import com.gf.snoopy.util.page.Page;
import com.gf.snoopy.util.page.PagedList;
import com.gf.snoopy.util.page.PaginationUtil;

import freemarker.template.Configuration;
import freemarker.template.Version;

@Service
public class TemplateService implements InitializingBean{

	@Resource
	private TemplateMapper templateMapper;
	
	@Resource
	private ProjectMapper projectMapper;
	
	@Resource
	private SubProjectMapper subProjectMapper;
	
	@Resource
	private ContentTemplateLoader contentTemplateLoader;
	
	@Value("${file_address}")
	private String file_address;
	
	@Value("${template_update_delay}")
	private String template_update_delay;
	
	@Value("${freemarker_version}")
	private String freemarker_version;
	
	private Configuration configuration = null;
	
	private static final Logger logger = Logger.getLogger(TemplateService.class);

	@Override
	public void afterPropertiesSet() throws Exception {
		configuration = new Configuration(new Version(freemarker_version));
		configuration.setDefaultEncoding("utf-8");
		configuration.setLocale(new Locale(SnoopyConstants.LOCAL_KEY));
		configuration.setTemplateLoader(contentTemplateLoader);
		configuration.setSetting("template_update_delay", template_update_delay);
	}
	
	/**
	 * 分页查询模板
	 * @param projectId
	 * @param query
	 * @param page
	 * @return
	 * @throws PokeException
	 */
	public PagedList<TemplateDto> getTemplatePage(TemplateDto query,Page page) throws PokeException{
		if(query==null || page==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"查询参数错误");
		}
		final Template dbQuery = ConvertUtil.convert(query,Template.class);
		PagedList<Template> voteInCondition =  PaginationUtil.pagedQuery(page, new Callable<List<Template>>() {            
            public List<Template> call() throws Exception {
                return templateMapper.queryByCondition(dbQuery);
            }
        });
		
		return new PagedList<TemplateDto>(voteInCondition.getPage(),
				ConvertUtil.convertList(voteInCondition.getList(),
						TemplateDto.class));
	}
	
	/**
	 * 新增模板
	 * @param data
	 * @throws PokeException
	 */
	public void save(TemplateDto data) throws PokeException{
		Template convert = ConvertUtil.convert(data, Template.class);
		if(convert==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"subProject数据出错");
		}
		templateMapper.insert(convert);
	}
	
	/**
	 * 保存文件
	 * @param templateType
	 * @param file
	 * @throws PokeException
	 */
	public void saveByMulti(TemplateType templateType,MultipartFile file) throws PokeException{
		if(templateType==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"模板类型为空");
		}
		if(file==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"模板文件为空");
		}
		if(!file.getOriginalFilename().contains(".ftl")){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"非法的模板文件，请上传ftl模板文件");
		}
		TemplateDto dto = new TemplateDto();
		dto.setName(file.getOriginalFilename());
		dto.setTemplateType(templateType.getCode());
		dto.setCreateDate(Calendar.getInstance().getTime());
		dto.setAddress(writeToFile(file));
		this.save(dto);
	}
	
	/**
	 * 
	 * @param id
	 * @return
	 * @throws PokeException
	 */
	public DownloadFile getFileByTemplateId(Integer id) throws PokeException{
		if(id==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"id为空");
		}
		Template template = templateMapper.selectByPrimaryKey(id);
		if(template==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"无法在数据库找到模板文件记录 id="+id);
		}
		File file = new File(template.getAddress());
		if(!file.exists()){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"无法找到模板文件 address="+template.getAddress());
		}
		return new DownloadFile(file.getName(),SnoopyUtil.convertFileToByte(file));
	}
	
	/**
	 * 根据Id删除记录
	 * @param id
	 * @throws PokeException
	 */
	public void deleteById(Integer id) throws PokeException{
		if(id==null){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"删除的subProjectId为空");
		}
		Template template = templateMapper.selectByPrimaryKey(id);
		File file = new File(template.getAddress());
		if(file.exists()){
			file.delete();
		}
		templateMapper.deleteByPrimaryKey(id);
	}
	
	public List<Template> findAllTemplate(){
		return templateMapper.queryByCondition(new Template());
	}
	
	/**
	 * 批量生成模板ZIP
	 * @param projectId
	 * @throws PokeException
	 */
	public DownloadFile generateAllFileByProjectId(Integer projectId) throws PokeException{
		Project project = projectMapper.selectByPrimaryKey(projectId);
		List<Template> parentTemplate = templateMapper.queryByType(Arrays
				.asList(TemplateType.SH.getCode(), TemplateType.SZ.getCode()));
		List<DownloadFile> resultFiles = new ArrayList<DownloadFile>();
		//生成所有母项目模板
		if(CollectionUtils.isNotEmpty(parentTemplate)){
			for(Template parent:parentTemplate){
				try{
					DownloadFile downloadFile = generateFileByProject(project, parent);
					resultFiles.add(downloadFile);
				}catch(Exception e){
					throw new PokeException(PokeErrorCode.INVALID_PARAM,"母模板("+parent.getName()+")生成出错"+e.getMessage());
				}
			}
		}
		//生成所有子项目模板
		List<Template> subTemplates = templateMapper.queryByType(Arrays
				.asList(TemplateType.SUB_SH.getCode(), TemplateType.SUB_SZ.getCode()));	
		List<SubProject> subProjects = subProjectMapper.queryByProjectId(projectId);
		if(CollectionUtils.isNotEmpty(subProjects)&&CollectionUtils.isNotEmpty(subTemplates)){
			for(SubProject subProject:subProjects){
				for(Template subTemplate:subTemplates){
					try{
						//子项目文件名要区分
						DownloadFile downloadFile = generateFileBySubProject(subProject, subTemplate);
						downloadFile.setFileName(subProject.getName()+"_"+downloadFile.getFileName());
						resultFiles.add(downloadFile);
					}catch(Exception e){
						throw new PokeException(PokeErrorCode.INVALID_PARAM,"子项目("+subProject.getName()+")生成子模板("+subTemplate.getName()+")出错："+e.getMessage());
					}					
				}
			}
		}
		
		return ZipUtil.zip(file_address,SnoopyConstants.ZIP_NAME, resultFiles);
	}
	
	/**
	 * 根据母项目Id和模板Id生成下载文件
	 * @param project
	 * @param fileTemplate
	 * @return
	 * @throws PokeException
	 */
	public DownloadFile generateFileByProjectId(Integer projectId,Integer templateId) throws PokeException{
		Project project = projectMapper.selectByPrimaryKey(projectId);
		Template fileTemplate = getFilePath(templateId);
		return generateFileByProject(project,fileTemplate);
	}
	
	/**
	 * 根据母项目和模板生成下载文件
	 * @param project
	 * @param fileTemplate
	 * @return
	 * @throws PokeException
	 */
	private DownloadFile generateFileByProject(Project project,Template fileTemplate) throws PokeException{	
		try {
			logger.info("----file template address:"+fileTemplate.getAddress());
			freemarker.template.Template template = configuration.getTemplate(file_address+fileTemplate.getName());
			Map<String, Object> templateData = ParseUtil.parseProject(project);
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			OutputStreamWriter writer = new OutputStreamWriter(outStream,"UTF-8");
			template.process(templateData, writer);
			byte[] word = outStream.toByteArray();
			writer.close();
			return new DownloadFile(parseFileName(fileTemplate.getName()),word);
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"模板生成出错");
		} 
	}
	
	/**
	 * 根据子项目Id和模板Id生成下载文件
	 * @param subProjectId
	 * @param templateId
	 * @return
	 * @throws PokeException
	 */
	public DownloadFile generateFileBySubProjectId(Integer subProjectId,Integer templateId) throws PokeException{
		Template fileTemplate = getFilePath(templateId);
		SubProject subProject = subProjectMapper.selectByPrimaryKey(subProjectId);
		return generateFileBySubProject(subProject,fileTemplate);
	}
	
	/**
	 * 根据子项目和模板生成下载文件
	 * @param subProject
	 * @param fileTemplate
	 * @return
	 * @throws PokeException
	 */
	private DownloadFile generateFileBySubProject(SubProject subProject,Template fileTemplate) throws PokeException{
		
		try {
			freemarker.template.Template template = configuration.getTemplate(file_address+fileTemplate.getName());			
			Map<String, Object> templateData = ParseUtil.parseSubProject(subProject);
			ByteArrayOutputStream outStream = new ByteArrayOutputStream();
			OutputStreamWriter writer = new OutputStreamWriter(outStream,"UTF-8");
			template.process(templateData, writer);
			byte[] word = outStream.toByteArray();
			writer.close();
			return new DownloadFile(parseFileName(fileTemplate.getName()),word);
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"模板生成出错");
		} 
	}
	
	private String writeToFile(MultipartFile file) throws PokeException{
		File f = new File(file_address+file.getOriginalFilename());
		if(f.exists()){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"已有同名文件!");
		}
		try {	
			InputStream inputStream = file.getInputStream();
			FileOutputStream outputStream = new FileOutputStream(f);
			SnoopyUtil.write(outputStream, inputStream);
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage(),e);
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"无法从本地获取文件");
		} catch (IOException e) {
			logger.error(e.getMessage(),e);
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"IO操作异常，请检查日志");
		}
		return f.getAbsolutePath();
	}
	
	/**
	 * 解析文件名
	 * @param fileName
	 * @return
	 */
	private String parseFileName(String fileName){
		if(fileName==null){
			return "未知文件";
		}
		String[] fileS = fileName.split("\\.");
		if(fileS.length!=2){
			return "未知文件";
		}
		String[] finalStr = fileS[0].split("_");
		if(finalStr.length<2){
			return finalStr[0];
		}
		return fileS[0].substring(0, fileS[0].lastIndexOf("_"))
				+ "."
				+ fileS[0].substring(fileS[0].lastIndexOf("_") + 1,
						fileS[0].length());
	}
	
	private Template getFilePath(Integer templateId) throws PokeException{
		Template template = templateMapper.selectByPrimaryKey(templateId);
		File file = new File(file_address+template.getName());
		if(!file.exists()){
			throw new PokeException(PokeErrorCode.INVALID_PARAM,"无法找到模板文件 address="+template.getAddress());
		}
		return template;
	}

}
